package com.infra.server.authorization;

import cn.hutool.core.convert.Convert;
import com.infra.server.utils.ConstantUtil;
import com.infra.server.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URI;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 鉴权管理器，用于判断是否有资源的访问权限
 *
 * @author zzd
 * @date 2020/6/19
 */
@Component
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    @Resource
    @Qualifier("redisTemplate")//指定导入的这个redisTemplate是我们重写的,默认是源码中的
    private RedisTemplate redisTemplate;
    @Resource
    private RedisUtil redisUtil;

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        ServerWebExchange exchange = authorizationContext.getExchange();
        //从Redis中获取当前路径可访问角色列表
        URI uri = exchange.getRequest().getURI();
        List<String> urlRoles = Convert.toList(String.class,redisUtil.hget(ConstantUtil.RESOURCE_ROLES_MAP,uri.getPath()));
        List<String> authorities = urlRoles.stream().map(i -> i = ConstantUtil.AUTHORITY_PREFIX + i).collect(Collectors.toList());

        // 判断当前接口是否存在redis中,即是否是权限接口,不是则直接放行
        boolean isExist = redisUtil.hHasKey(ConstantUtil.RESOURCE_ROLES_MAP,uri.getPath());
        if(!isExist){
            return mono.just(new AuthorizationDecision(true));
        }

        String adminRole = ConstantUtil.AUTHORITY_PREFIX+ConstantUtil.ADMINISTRATOR_ROLE;

        //认证通过且角色匹配的用户可访问当前路径,管理员无条件通过
        return mono
                .filter(Authentication::isAuthenticated)
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                .any(a -> {
                    String[] roles = a.split(",");
                    for(String role:roles) {
                        if(authorities.contains(role) || adminRole.equals(role)) {
                            return true;
                        }
                    }
                    return false;
                })
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
    }

}
